package com.example.demo5072.util;

import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * @author lzh
 * @date 2019/8/2 - 9:19
 */
public class ZipUtil {

    /**
     * zip输入流 --- 解压zip
     */
    private ZipInputStream zipIn;
    /**
     * zip输出流 --- 压缩ZIP
     */
    private ZipOutputStream zipOut;

    /**
     * ZipEntry
     * 在每一个压缩文件中都会存在多个子文件，
     * 那么这每一个的子文件在JAVA中就使用ZipEntry表示
     */
    private ZipEntry zipEntry;

    /**
     * 缓冲区大小
     */
    private   int bufSize;


    /**
     * buf缓冲区  要读取/写入的数据
     */
    private byte[]  buf;

    /**
     * 已读取字节数
     */
    private int  readedBytes;

    public ZipUtil(int bufSize){
        this.bufSize = bufSize;
        this.buf = new byte[this.bufSize];
    }

    public ZipUtil(){
        this(512);
    }

    public void setBufSize(int bufSize) {
        this.bufSize = bufSize;
    }

    /**
     * 递归处理文件读取
     * @param dir     要读取的目录
     * @param zipOut   zip输出流
     * @throws Exception
     */
    private void handleDir(File dir ,ZipOutputStream zipOut) throws Exception{
        FileInputStream fileIn;
        File[] files;

        files = dir.listFiles();

        if(files.length == 0){
            //如果目录为空,则单独创建之.
            //ZipEntry的isDirectory()方法中,目录以"/"结尾.

            //在压缩文件中，每一个压缩的内容都可以用一个ZipEntry表示，
            //所以在进行压缩之前必须通过putNextEntry设置一个ZipEntry即可
            this.zipOut.putNextEntry(new ZipEntry(dir.toString() + "/"));
            this.zipOut.closeEntry();
        }
        else{//如果目录不为空,则分别处理目录和文件.
            for(File fileName : files){
                //System.out.println(fileName);

                if(fileName.isDirectory()){
                    //递归处理文件读取
                    handleDir(fileName , this.zipOut);
                }
                else{
                    fileIn = new FileInputStream(fileName);
                    this.zipOut.putNextEntry(new ZipEntry(fileName.toString()));

                    while((this.readedBytes = fileIn.read(this.buf))>0){
                        this.zipOut.write(this.buf , 0 , this.readedBytes);
                    }

                    this.zipOut.closeEntry();
                }
            }
        }
    }


    /**
     * 压缩为ZIP
     *
     * 在压缩文件中，每一个压缩的内容都可以用一个ZipEntry表示，
     * 所以在进行压缩之前必须通过putNextEntry设置一个ZipEntry即可
     * @param zipDirectory
     */
    public void doZip(String zipDirectory){
        File file;
        File zipDir;

        zipDir = new File(zipDirectory);
        //压缩后生成的zip文件名
        String zipFileName = zipDir.getName() + ".zip";

        try{
            this.zipOut = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFileName)));
            handleDir(zipDir , this.zipOut);
            this.zipOut.close();
        }catch(Exception ioe){
            ioe.printStackTrace();
        }

    }


    /**
     * 解压指定zip文件
     * @param unZipFileName  需要解压的zip文件名
     */
    public void unZip(String unZipFileName){


        //使用FileOutputStream流可以写入字节数据到目标文件，
        // FileOutputStream提供了单字节写入和byte数组写入两种方式。
        // 建议使用byte数组写入，将待写入的数据存储到一个byte数组中，然后再写入文件。
        // 当写入的文件已经存在时，需要指明写入方式是覆盖还是追加
        FileOutputStream fileOut;

        //Java文件类以抽象的方式代表文件名和目录路径名。
        // 该类主要用于文件和目录的创建、文件的查找和文件的删除等。
        //File对象代表磁盘中实际存在的文件和目录
        File file;

        try{
            this.zipIn = new ZipInputStream (new BufferedInputStream(new FileInputStream(unZipFileName)));

            while((this.zipEntry = this.zipIn.getNextEntry()) != null){
                file = new File(this.zipEntry.getName());
                //System.out.println(file);///

                if(this.zipEntry.isDirectory()){
                    file.mkdirs();
                }
                else{
                    //如果指定文件的目录不存在,则创建之.
                    File parent = file.getParentFile();
                    if(!parent.exists()){
                        parent.mkdirs();
                    }

                    fileOut = new FileOutputStream(file);
                    while(( this.readedBytes = this.zipIn.read(this.buf) ) > 0){
                        fileOut.write(this.buf , 0 , this.readedBytes );
                    }
                    fileOut.close();
                }
                this.zipIn.closeEntry();
            }
        }catch(IOException ioe){
            ioe.printStackTrace();
        }
    }





    //测试Zip类
    public static void main(String[] args)throws Exception{
        if(args.length==2){
            String name = args[1];
            ZipUtil zip = new ZipUtil();

            if(args[0].equals("-zip")){
                zip.doZip(name);
            }
            else if(args[0].equals("-unzip")){
                zip.unZip(name);
            }
        }
        else{
            System.out.println("Usage:");
            System.out.println("压缩:java Zip -zip http://www.bt285.cn /directoryName");
            System.out.println("解压:java Zip -unzip http://www.feng123.com /fileName.zip");
            throw new Exception("Arguments error!");
        }
    }




















}
